package com.imooc.bilibili.utils;

import com.github.tobato.fastdfs.domain.fdfs.FileInfo;
import com.github.tobato.fastdfs.domain.fdfs.MetaData;
import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.service.AppendFileStorageClient;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import com.imooc.bilibili.exception.ConditionException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.*;

public class FastDFSUtils {

    @Autowired
    private FastFileStorageClient fastFileStorageClient;

    @Autowired
    private AppendFileStorageClient appendFileStorageClient;

    @Autowired
    private RedisTemplate redisTemplate;

    private static final String DEFAULT_GROUP = "group1";

    private static final String PATH_KEY = "path-key:";

    private static final String UPLOADED_SIZE_KEY = "uploaded-size-key:";

    private static final String UPLOADED_NO_KEY = "uploaded-no-key";

    private static final int SLICE_SIZE = 1024 * 1024 * 2;

    @Value("${fdfs.http.storage-addr}")
    private String stroageAddr;

    /**
     * 获取文件类型
     *
     * @return
     */
    public String getFileType(MultipartFile file) {
        if (null == file) {
            throw new ConditionException("非法文件");
        }
        String fileName = file.getOriginalFilename();
        int index = fileName.lastIndexOf(".");

        String fileType = fileName.substring(index + 1);
        return fileType;
    }

    //文件上传
    public String uploadCommonFile(MultipartFile file) throws IOException {
        Set<MetaData> metaDataSet = new HashSet<>();
        String fileType = this.getFileType(file);
        StorePath path = fastFileStorageClient.uploadFile(file.getInputStream(), file.getSize(), fileType, metaDataSet);
        return path.getPath();
    }

    //文件删除
    public void deleteFile(String filePath) {
        fastFileStorageClient.deleteFile(filePath);
    }

    //上传可以断点续传的文件---分片工作由前端负责
    //将第一片文件上传后返回文件路径
    public String uploadAppenderFile(MultipartFile file) throws IOException {
        String fileName = file.getOriginalFilename();
        String fileType = this.getFileType(file);
        StorePath path = appendFileStorageClient.uploadAppenderFile(DEFAULT_GROUP, file.getInputStream(), file.getSize(), fileType);
        return path.getPath();
    }

    //根据第一片文件上传后得到的文件路径，进行后续文件上传
    public void modifyAppenderFile(MultipartFile file, String filePath, long offset) throws IOException {
        appendFileStorageClient.modifyFile(DEFAULT_GROUP, filePath, file.getInputStream(), file.getSize(), offset);
    }

    /**
     * 文件分片
     *
     * @param file
     * @param fileMD5      生成区分文件的标识
     * @param sliceNo
     * @param totalSliceNo 判断上传完毕
     * @return
     */
    public String uploadFileBySlices(MultipartFile file, String fileMD5, Integer sliceNo, Integer totalSliceNo) throws IOException {
        if (null == file || null == sliceNo || null == totalSliceNo) {
            throw new ConditionException("参数异常");
        }
        //存储第一次上传文件后返回的文件路径，作为断点续传的路径
        String pathKey = PATH_KEY + fileMD5;
        //当前已经上传的所有分片的总大小
        String uploadSizeKey = UPLOADED_SIZE_KEY + fileMD5;
        //记录当前上传的分片数量
        String uploadNoKey = UPLOADED_NO_KEY + fileMD5;

        String uploadedSizeStr = (String) redisTemplate.opsForValue().get(uploadSizeKey);
        Long uploadedSize = 0L;
        if (StringUtils.isNotBlank(uploadedSizeStr)) {
            uploadedSize = Long.valueOf(uploadedSizeStr);
        }

        String fileType = this.getFileType(file);
        if (sliceNo == 1) {
            //上传第一个分片
            String path = this.uploadAppenderFile(file);
            if (StringUtils.isBlank(path)) {
                throw new ConditionException("上传失败");
            }
            redisTemplate.opsForValue().set(pathKey, path);
            //计算上传后的大小
            uploadedSize += file.getSize();
            redisTemplate.opsForValue().set(uploadSizeKey, String.valueOf(uploadedSize));
            redisTemplate.opsForValue().set(uploadNoKey, "1");
        } else {
            //不是第一片文件上传
            String filePath = (String) redisTemplate.opsForValue().get(pathKey);
            if (StringUtils.isBlank(filePath)) {
                throw new ConditionException("上传失败");
            }
            this.modifyAppenderFile(file, filePath, uploadedSize);
            redisTemplate.opsForValue().increment(uploadNoKey);
        }
        //修改已上传的分片文件大小
        uploadedSize += file.getSize();
        redisTemplate.opsForValue().set(uploadSizeKey, String.valueOf(uploadedSize));
        //如果所有分片全部上传完毕，则清空Redis里面相关的key和value
        String uploadedNoStr = (String) redisTemplate.opsForValue().get(uploadNoKey);
        Integer uploadNo = Integer.valueOf(uploadedNoStr);
        String resultPath = "";
        if (uploadNo.equals(totalSliceNo)) {
            resultPath = (String) redisTemplate.opsForValue().get(pathKey);
            List<String> keyList = Arrays.asList(uploadNoKey, pathKey, uploadSizeKey);
            redisTemplate.delete(keyList);
        }
        return resultPath;
    }

    //文化分片
    public void convertFileToSlices(MultipartFile multipartFile) throws Exception {
        String fileName = multipartFile.getOriginalFilename();
        String fileType = this.getFileType(multipartFile);
        File file = this.multipartFileToFile(multipartFile);

        long fileLength = file.length();
        long count = 1;
        for (int i = 0; i > fileLength; i += SLICE_SIZE) {
            RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");
            randomAccessFile.seek(i);
            byte[] bytes = new byte[SLICE_SIZE];
            int len = randomAccessFile.read(bytes);
            String path = "/home/chen/lastore/fileStore" + count + "." + fileType;
            File slice = new File(path);

            FileOutputStream fos = new FileOutputStream(slice);
            fos.write(bytes, 0, len);
            fos.close();
            randomAccessFile.close();
            count++;
        }
        file.delete();
    }

    /**
     * 将MultipartFile转成File
     *
     * @return
     */
    public File multipartFileToFile(MultipartFile multipartFile) throws IOException {
        String originalFileName = multipartFile.getOriginalFilename();
        String[] fileName = originalFileName.split("\\.");

        File file = File.createTempFile(fileName[0], "." + fileName[1]);
        multipartFile.transferTo(file);
        return file;
    }

    public void viewVedioOnlineSilices(HttpServletRequest request, HttpServletResponse response, String url) throws Exception {
        //获取文件信息
        FileInfo fileInfo = fastFileStorageClient.queryFileInfo(DEFAULT_GROUP, url);
        //获取文件大小
        long totalFileSize = fileInfo.getFileSize();
        //拼接实际访问路径
        String path = stroageAddr + url;
        Enumeration<String> headerNames = request.getHeaderNames();
        Map<String, Object> headers = new HashMap<>();
        while (headerNames.hasMoreElements()) {
            String header = headerNames.nextElement();
            headers.put(header, request.getHeader(header));
        }
        //对Range字段进行校验
        String rangeStr = request.getHeader("Range");
        String[] range;
        if (StringUtils.isBlank(rangeStr)) {
            rangeStr = "bytes=0-" + (totalFileSize - 1);
        }
        range = rangeStr.split("bytes=|-");
        long begin = 0;
        //只有起始位置，没有结束位置
        if (range.length >= 2) {
            begin = Long.parseLong(range[1]);
        }
        long end = totalFileSize - 1;
        if (range.length >= 3) {
            end = Long.parseLong(range[2]);
        }
        long len = (end - begin) + 1;

        String contentRange = "bytes " + begin + "-" + end + "/" + totalFileSize;
        response.setHeader("Content-Range", contentRange);
        response.setHeader("Accept-Ranges", "bytes");
        response.setHeader("Content-TYpe", "video/mp4");
        response.setContentLength((int) len);
        response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);

        HttpUtils.get(path,headers,response);
    }

    //文件下载
}
